今天要來做一個可以使用 input 上傳檔案,也可以 drag and drop 檔案的範例~
不過現在做的只是可以拿到檔案的名稱顯示而已喔!
input 上傳檔案:
drag and drop拖拉上傳檔案:
input[type="file"]
input的type="file"
是可以上傳檔案的,不過因為他本人長得有點抱歉,而且也不太能改動,所以我會把他隱藏起來再另外做一個。
html:
<div class="container">
<input class="file-upload-input" name="upload-file" type="file" />
<div class="file-upload-zone">
<a href="#" class="file-upload__btn">upload file</a>
<span class="file-upload__name"></span>
</div>
</div>
先把input { display: none }
,
再另外做一個 <a>
連結來做一個上傳檔案的button樣式,不過我們依然需要他被點擊打開資料夾選擇檔案的畫面。
這邊學到可以利用 HTMLElement.click()
直接在<a>
的 click 事件裡面再去 input.click()
,就可以等於點擊到被隱藏的 input
MDN:
HTMLElement.click() 方法可以模擬滑鼠點擊一個元素。
當 click() 被使用在支援的元素(例如任一<input>
元素),便會觸發該元素的點擊事件。事件會冒泡至 document tree(或 event chain)的上層元素,並觸發它們的點擊事件。其中的例外是:click()
方法不會讓<a>
元素和接收到真實滑鼠點擊一樣進行頁面跳轉。
最後一句話 「click()
方法不會讓 <a>
元素和接收到真實滑鼠點擊一樣進行頁面跳轉。」 所以在這裡不需要使用 e.preventDefault()
來防止 <a>
的預設跳轉事件。
JavaScript:
const fileInput = document.querySelector(".file-upload-input");
const fileBtn = document.querySelector(".file-upload__btn");
fileBtn.addEventListener("click", function (e) {
fileInput.click();
});
input[type="file"]
選起來 => fileInput<a>
按鈕也選起來 => fileBtnfileInput.click()
input[type="file"]
如何拿到檔案的名稱雖然是按假的按鈕,實際上還是在操作 input
,而input[type='file']
的監聽事件是 change
,只要有新檔案進來對input來說就是一個改變。
所以還是必須對 input
來做監聽事件,才能拿到新檔案的名稱。
記得昨天提到的Listener的參數 event
有一堆屬性嗎?檔案名稱也可以從裡面拿到,不過這只有input[type='file']
專屬的屬性,所以要用event.target
去拿到。
input's HTMLInputElement.files
來試試看把 e.target.files
印出來看看長怎樣:
看起來是個類陣列,也有檔案的名稱顯示在裡面,就可以使用files[0].name
來拿到檔案的名稱,再把這個名稱放到要呈現檔案名稱的tag就好了!
const fileInput = document.querySelector(".file-upload-input");
const fileNameZone = document.querySelector(".file-upload__name");
fileInput.addEventListener("change", function (e) {
const fileName = e.target.files[0].name;
fileNameZone.textContent = fileName;
});
成果:
drag, drop 也是監聽事件的名稱,但是分為好幾種,這邊只列出這次會用到的部分:
事件 | 會如何被觸發 |
---|---|
dragenter | The dragged item is dragged over dropArea, making it the target for the drop event if the user drops it there. 當被拖拉的項目經過有drop監聽事件的區域,當這個項目被drop下來就會當作目標 |
dragleave | The dragged item is dragged off of dropArea and onto another element, making it the target for the drop event instead . 當被拖拉的項目離開有drop監聽的區域或是拖拉到其他地方的時候觸發,會讓這個項目變成要被drop的目標 |
dragover | Every few hundred milliseconds, while the dragged item is over dropArea and is moving. 每一百毫秒觸一次,當被拖拉的項目經過drop地區的時候 |
drop | The user releases their mouse button, dropping the dragged item onto dropArea. 當使用者放開滑鼠並且被拖拉的項目是在drop區域的時候 |
出自:https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/
當我在進行這一段的時候,本來以為我只要監聽要被 drop 的地方而已,但有 drop 就一定會有 drag , drop 區域必須也要有監聽 drag 的所有事件才能有連帶反應。
而且必須在這些事件中加 e.preventDefault()
來阻止瀏覽器的預設行為,例如我現在要drag drop一個圖片上去,可是一直上不去是因為瀏覽器會自動幫我打開圖片。
為了防止瀏覽器的預設行為,都必須加上 e.preventDefault()
。
const dropZone = document.querySelector(".file-upload-zone");
dropZone.addEventListener("dragenter", function (e) {
e.preventDefault();
});
dropZone.addEventListener("dragover", function (e) {
e.preventDefault();
});
dropZone.addEventListener("dragleave", function (e) {
e.preventDefault();
});
如果在各個監聽事件加上
console.log('dragover')
or'dragenter'
,打開devTool就會看到很多dragover
在那邊跑 XD 可以用這方式來確認是否有監聽到。
這裡就完全跟 input 沒關係了,不過幸好 drag drop 也有提供可以拿到檔案的屬性,每個 drag event 都有 dataTransfer
的屬性可以使用。
不管加上哪一個 drag drop 監聽事件都有!不過要拿到檔案名稱的話還是要放在 drop
裏面才會被記錄下來。
console.log(e.dataTransfer)
可是打開files
裡面居然沒有東西?後來看到這個:https://howtojs.io/empty-files-in-event-datatransfer-in-drop-event-in-javascript/
由於 Google 控制台的錯誤,您很可能會得到一個空數組。無需將 event.dataTransfer 記錄到控制台並展開它以查看 files 數組,您可以使用 event.dataTransfer.files 直接將 files 數組記錄到控制台,它會為您提供數組的長度以及 files 列表。
有可能會看到一個空陣列,可是其實可以直接用files打開:
console.log(e.dataTransfer.files)
拿到圖片的資訊了!所以不要相信那個空陣列,直接.files
打開看看裡面的東西
實驗做完就可以來把這個名稱拿出來,放在要顯示檔案名稱的地方了:
dropZone.addEventListener("drop", function (e) {
e.preventDefault();
const fileName = e.dataTransfer.files[0].name;
fileNameZone.textContent = fileName;
});
成果:
codepen:https://codepen.io/Jadddxx/pen/oNdaQKR?editors=0110
input.click()
來打開。e.preventDefault()
來取消瀏覽器的預設行為,否則有可能檔案會自動被打開。e.dataTransfer
的陣列有可能是空的,但不要相信他,直接 e.dataTransfer.files
打開看看。e.dataTransfer.files
要在drop的監聽事件裏面才能會記錄到。參考資料:
MDN HTMLElement: drop event
MDN Using files from web applications
MDN HTMLElement.click()
MDN File drag and drop
Is call to preventDefault() really necessary on drop event?
JavaScript 中的 drop 事件中 event.dataTransfer 中的空文件
How To Make A Drag-and-Drop File Uploader With Vanilla JavaScript
What propagation I'm stopping in drag and drop?